using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Runtime.CompilerServices;
using NUnit.Framework;

namespace DT.Art.VFX.DTEditorGUI
{
    #region 扩展DTGUIEx类
    public static partial class DTGUIEx
    {

        public static int kVerticalSpacingMultiField => 2;
        public static float kLineHeight => 18;


        #region  基本绘制 
        //Float : float,slider,Popup,toggle,button
        //Color : Color
        //vector: vector2,vector2 + vector2,vector3,vector4, [float的部分，btns]
        public static void SetValue(this MaterialProperty prop, DTchannelDate value, ChannelMode channelMode = ChannelMode.none)
        {
            if (prop == null) return;
            if (prop.type == MaterialProperty.PropType.Float || prop.type == MaterialProperty.PropType.Range)
            {
                prop.floatValue = value.ToFloat(channelMode);
            }
            else if (prop.type == MaterialProperty.PropType.Int)
            {
                prop.intValue = value.ToInt(channelMode);
            }
            else if (prop.type == MaterialProperty.PropType.Color)
            {
                prop.colorValue = value.ToColor();
            }
            else if (prop.type == MaterialProperty.PropType.Vector)
            {
                prop.vectorValue = value.ToVector4();
            }
            else if (prop.type == MaterialProperty.PropType.Texture)
            {
                prop.textureScaleAndOffset = value.ToVector4();
            }
        }
        public static DTchannelDate GetValue(this MaterialProperty prop)
        {
            if (prop == null) return new DTchannelDate();
            return new DTchannelDate(prop);
        }
        public static void DrawField<T>(this MaterialEditor editor, MaterialProperty prop, Func<Rect> rectf = null,
                float width = -1, bool isEditor = false,
                DTFieldArgs<T> args = null
        )
        {
            MaterialProperty property = prop;
            if (property == null) return;
            if (args == null) args = new DTFieldArgs<T>();
            args.Check();
            if (rectf == null) rectf = () => { return DTGUIEx.GetRect(DTGUIEx.kLineHeight); };
            int indent = EditorGUI.indentLevel;
            // EditorGUI.indentLevel = 0; ;
            EditorGUI.BeginChangeCheck();
            EditorGUI.showMixedValue = property.hasMixedValue;
            DTchannelDate tmp = property.GetValue();
            T val = (T)args.InDeCode(tmp);
            T newValue = val;
            if (isEditor)
            {
                newValue = args.DrawEditorGUI(val);
            }
            else
            {
                newValue = args.DrawGUI(rectf(), val);
            }

            EditorGUI.showMixedValue = false;
            if (EditorGUI.EndChangeCheck() || !newValue.Equals(val))
            {
                editor.RegisterPropertyChangeUndo(property.displayName);
                val = newValue;
                args.Callback?.Invoke(val);
                args.OutDeCode(val, property);
            }
            EditorGUI.indentLevel = indent;
            // MaterialEditor.EndProperty();
        }
        #endregion
        #region  FLOAT

        public static void DrawFloatField(this MaterialEditor editor, MaterialProperty prop, GUIContent label,
                ChannelMode channleIndex = ChannelMode.none, bool isE = false, Action<float> callback = null)
        {
            if (prop == null) return;
            DrawField<float>(editor, prop, isEditor: isE, args: new DTFieldArgs<float>()
            {
                InDeCode = (val) => { return val.ToFloat(channleIndex); },
                OutDeCode = (obj, prop) => { prop.SetValue(prop.GetValue().Set(channleIndex, (float)obj), channleIndex); },
                DrawGUI = (rect, val) => { return EditorGUI.FloatField(rect, label, val); },
                DrawEditorGUI = (val) =>
                {
                    return EditorGUILayout.FloatField(label, val);
                },
                Callback = callback
            });
        }

        public static void DrawFloatField(this MaterialEditor editor, MaterialProperty prop, GUIContent label, float minmaxValue, bool isMax = false,
                ChannelMode channleIndex = ChannelMode.none, bool isE = false, Action<float> callback = null)
        {
            if (prop != null)
            {
                DrawField<float>(editor, prop, isEditor: isE, args: new DTFieldArgs<float>()
                {
                    InDeCode = (val) => { return val.ToFloat(channleIndex); },
                    OutDeCode = (obj, prop) => { prop.SetValue(prop.GetValue().Set(channleIndex, (float)obj), channleIndex); },
                    DrawGUI = (rect, val) =>
                    {
                        float v = EditorGUI.FloatField(rect, label, val);
                        return isMax ? Mathf.Min(v, minmaxValue) : Mathf.Max(v, minmaxValue);
                    },
                    DrawEditorGUI = (val) =>
                    {
                        float v = EditorGUILayout.FloatField(label, val);
                        return isMax ? Mathf.Min(v, minmaxValue) : Mathf.Max(v, minmaxValue);
                    },
                    Callback = callback
                });
            }
        }
        public static void DrawFloatField(this MaterialEditor editor, MaterialProperty prop, GUIContent label, float min, float max,
            ChannelMode channleIndex = ChannelMode.none, bool isE = false, Action<float> callback = null)
        {
            if (prop == null) return;
            DrawField<float>(editor, prop, isEditor: isE, args: new DTFieldArgs<float>()
            {
                InDeCode = (val) => { return val.ToFloat(channleIndex); },
                OutDeCode = (obj, prop) => { prop.SetValue(prop.GetValue().Set(channleIndex, (float)obj), channleIndex); },
                DrawGUI = (rect, val) =>
                {
                    float v = EditorGUI.FloatField(rect, label, val);
                    return Mathf.Min(max, Mathf.Max(v, min));
                },
                DrawEditorGUI = (val) =>
                {
                    float v = EditorGUILayout.FloatField(label, val);
                    return Mathf.Min(max, Mathf.Max(v, min));
                },
                Callback = callback
            });
        }

        public static void DrawRangeField(this MaterialEditor editor, MaterialProperty prop, GUIContent label, float min = 0, float max = 1,
            ChannelMode channleIndex = ChannelMode.none, bool isE = false, Action<float> callback = null)
        {
            if (prop == null) return;
            DrawField<float>(editor, prop, isEditor: isE, args: new DTFieldArgs<float>()
            {
                InDeCode = (val) => { return val.ToFloat(channleIndex); },
                OutDeCode = (obj, prop) => { prop.SetValue(prop.GetValue().Set(channleIndex, (float)obj), channleIndex); },
                DrawGUI = (rect, val) => { return EditorGUI.Slider(rect, label, val, min, max); },
                DrawEditorGUI = (val) =>
                {
                    GUI.backgroundColor = Color.white;
                    return EditorGUILayout.Slider(label, val, min, max);
                },
                Callback = callback
            });
        }
        #endregion
        #region  COLOR
        public static void DrawColorField(this MaterialEditor editor, MaterialProperty prop, GUIContent label, bool showAlpha = true, bool hdr = false, ChannelMode channleIndex = ChannelMode.none, Action<Color> callback = null)
        {
            if (prop == null) return;
            DrawField<Color>(editor, prop, args: new DTFieldArgs<Color>()
            {
                InDeCode = (val) => { return val.ToColor(); },
                OutDeCode = (obj, prop) => { prop.SetValue(new DTchannelDate(obj)); },
                DrawGUI = (rect, val) => { return EditorGUI.ColorField(rect, label, val, true, showAlpha, hdr); },
                Callback = callback
            });
        }
        #endregion
        #region  VECTOR        
        public static void DrawVector4Field(this MaterialEditor editor, MaterialProperty prop, GUIContent label, Action<Vector4> callback = null)
        {
            if (prop.type != MaterialProperty.PropType.Vector && prop.type != MaterialProperty.PropType.Color && prop.type != MaterialProperty.PropType.Texture)
            {
                throw new Exception("DrawFloatField only support Vector type");
            }
            DrawField<Vector4>(editor, prop, args: new DTFieldArgs<Vector4>()
            {
                InDeCode = (prop) => { return prop.ToVector4(); },
                OutDeCode = (obj, prop) => { prop.SetValue(new DTchannelDate(obj)); },
                DrawGUI = (rect, val) => { return EditorGUI.Vector4Field(rect, label, val); },
                Callback = callback
            });
        }
        public static void DrawVector3Field(this MaterialEditor editor, MaterialProperty prop, GUIContent label,
                    ChannelMode channleIndex = ChannelMode.x | ChannelMode.y | ChannelMode.z, Action<Vector3> callback = null)
        {
            if (prop.type != MaterialProperty.PropType.Vector)
            {
                throw new Exception("DrawFloatField only support Vector type");
            }
            DrawField<Vector3>(editor, prop, args: new DTFieldArgs<Vector3>()
            {
                InDeCode = (prop) => { return prop.ToVector3(channleIndex); },
                OutDeCode = (obj, prop) => { prop.SetValue(prop.GetValue().Set(channleIndex, obj.x, obj.y, obj.z)); },
                DrawGUI = (rect, val) => { return EditorGUI.Vector3Field(rect, label, val); },
                Callback = callback
            });
        }
        public static void DrawVector2Field(this MaterialEditor editor, MaterialProperty prop, GUIContent label,
                        ChannelMode channleIndex = ChannelMode.x | ChannelMode.y, Action<Vector2> callback = null)
        {
            if (prop.type != MaterialProperty.PropType.Vector)
            {
                throw new Exception("DrawFloatField only support Vector type");
            }
            DrawField<Vector2>(editor, prop, args: new DTFieldArgs<Vector2>()
            {
                InDeCode = (prop) => { return prop.ToVector2(channleIndex); },
                OutDeCode = (obj, prop) => { prop.SetValue(prop.GetValue().Set(channleIndex, obj.x, obj.y)); },
                DrawGUI = (rect, val) => //{ return EditorGUI.Vector2Field(rect, label, val); }
                {
                    int indent = EditorGUI.indentLevel;
                    EditorGUI.indentLevel = 0;
                    float w = EditorGUIUtility.labelWidth - 15 * indent;
                    Rect lablerect = new Rect(rect.x + 15 * indent, rect.y, w, rect.height);
                    Rect valrect = new Rect(rect.x + w + 15 * indent, rect.y, rect.width - w - 15 * indent, rect.height);
                    EditorGUI.LabelField(lablerect, label);
                    Vector2 vector2 = EditorGUI.Vector2Field(valrect, GUIContent.none, val);
                    EditorGUI.indentLevel = indent;
                    return vector2;
                },
                Callback = callback
            });
        }

        public static void DrawVector2FieldTex(this MaterialEditor editor, MaterialProperty prop, GUIContent label,
                    ChannelMode channleIndex = ChannelMode.x | ChannelMode.y, Action<Vector2> callback = null)
        {
            if (prop.type != MaterialProperty.PropType.Texture)
            {
                throw new Exception("DrawFloatField only support Texture type");
            }
            DrawField<Vector2>(editor, prop, args: new DTFieldArgs<Vector2>()
            {
                InDeCode = (prop) => { return prop.ToVector2(channleIndex); },
                OutDeCode = (obj, prop) => { prop.SetValue(prop.GetValue().Set(channleIndex, obj.x, obj.y)); },
                DrawGUI = (rect, val) =>
                {
                    int indent = EditorGUI.indentLevel;
                    EditorGUI.indentLevel = 0;
                    float w = EditorGUIUtility.labelWidth - 15 * indent;
                    Rect lablerect = new Rect(rect.x + 15 * indent, rect.y, w, rect.height);
                    Rect valrect = new Rect(rect.x + w + 15 * indent, rect.y, rect.width - w - 15 * indent, rect.height);
                    EditorGUI.LabelField(lablerect, label);
                    Vector2 vector2 = EditorGUI.Vector2Field(valrect, GUIContent.none, val);
                    EditorGUI.indentLevel = indent;
                    return vector2;
                },
                Callback = callback
            });
        }
        #endregion

        #region Toggle
        // public static void DrawToggleField(this MaterialEditor editor, MaterialProperty prop, GUIContent label, ChannelMode channleIndex = ChannelMode.none, bool isE = false,)
        // public static void ToggleShaderProperty(MaterialEditor editor, Rect rect, MaterialProperty prop, GUIContent label, DECodeArgs<int> args = null)
        // {
        //     if (args == null) args = new DECodeArgs<int>();
        //     args.Check();
        //     if (prop.type != MaterialProperty.PropType.Texture)
        //     {
        //         throw new Exception("DrawFloatField only support Texture type");
        //     }
        //     MaterialEditor.BeginProperty(prop);
        //     /*
        //     */
        //     EditorGUI.BeginChangeCheck();
        //     if (EditorGUI.EndChangeCheck())
        //     {
        //         editor.RegisterPropertyChangeUndo(label.text);
        //     }
        //     MaterialEditor.EndProperty();
        // }
        #endregion
        #region Popup
        public static void DrawEnumFlagsField<T>(this MaterialEditor editor, MaterialProperty prop, GUIContent label,
                     ChannelMode channleIndex = ChannelMode.none, DECodeArgs<int> args = null, Action<int> callback = null) where T : Enum
        {
            if (prop == null) return;
            if (args == null) args = new DECodeArgs<int>();
            args.Check();
            DrawField<int>(editor, prop, args: new DTFieldArgs<int>()
            {
                InDeCode = (val) => { return (int)args.InDeCode(val.ToInt(channleIndex)); },
                OutDeCode = (obj, prop) => { prop.SetValue(prop.GetValue().Set(channleIndex, (int)args.OutDeCode(obj))); },
                DrawGUI = (rect, val) =>
                {
                    T enumvalue = (T)(object)val;
                    return (int)(object)EditorGUI.EnumFlagsField(rect, label.text, enumvalue);
                },
                DrawEditorGUI = null,
                Callback = callback
            });
        }

        public static void DrawPopupField(this MaterialEditor editor, MaterialProperty prop, GUIContent label, string[] displayedOptions,
                                            ChannelMode channleIndex = ChannelMode.none, int index = -1, int len = -1, float width = -1, bool isEd = false, DECodeArgs<int> args = null)
        {
            GUIContent[] options = new GUIContent[displayedOptions.Length];
            for (int i = 0; i < displayedOptions.Length; i++)
            {
                options[i] = new GUIContent(displayedOptions[i]);
            }
            DrawPopupField(editor, prop, label, options, channleIndex, index, len, width, isEd, args);
        }

        public static void DrawPopupField(this MaterialEditor editor, MaterialProperty prop, GUIContent label, GUIContent[] displayedOptions,
                                            ChannelMode channleIndex = ChannelMode.none, int index = -1, int len = -1, float width = -1, bool isEd = false,
                                            DECodeArgs<int> args = null, Action<int> callback = null)
        {
            if (prop == null) return;
            if (args == null) args = new DECodeArgs<int>();
            args.Check();
            DrawField<int>(editor, prop, width: width, isEditor: isEd, args: new DTFieldArgs<int>()
            {
                InDeCode = (prop) =>
                {
                    int val = prop.ToInt(channleIndex);
                    val = DTUtil.GetBits(val, index, len);
                    return (int)args.InDeCode(val);
                },
                OutDeCode = (obj, prop) =>
                {
                    DTchannelDate tmp = prop.GetValue();
                    int val = (int)args.OutDeCode(obj);
                    val = DTUtil.ReplaceBits(val, tmp.ToInt(channleIndex), index, len);
                    tmp.Set(channleIndex, val);
                    prop.SetValue(tmp, channleIndex);
                },
                DrawGUI = (rect, val) =>
                {
                    if (width < 0)
                        return EditorGUI.Popup(rect, label, val, displayedOptions);
                    else
                    {
                        rect.width = width;
                        int indent = EditorGUI.indentLevel;
                        EditorGUI.indentLevel = 0;
                        int v = EditorGUI.Popup(rect, GUIContent.none, val, displayedOptions, new GUIStyle(EditorStyles.miniButton) { imagePosition = ImagePosition.ImageOnly, alignment = TextAnchor.MiddleCenter });
                        EditorGUI.indentLevel = indent;
                        return v;
                    }
                },
                DrawEditorGUI = (val) =>
                {
                    if (width < 0)
                        return EditorGUILayout.Popup(label, val, displayedOptions);
                    else
                    {
                        int indent = EditorGUI.indentLevel;
                        EditorGUI.indentLevel = 0;
                        int v = EditorGUILayout.Popup(label, val, displayedOptions,
                            new GUIStyle(EditorStyles.popup) { imagePosition = ImagePosition.ImageOnly, alignment = TextAnchor.MiddleCenter }
                            , GUILayout.Width(width), GUILayout.ExpandWidth(false));
                        EditorGUI.indentLevel = indent;
                        return v;
                    }
                },
                Callback = callback
            });
        }

        #endregion
        #region Button 
        public static void ToggleBTNShaderProperty(this MaterialEditor editor, MaterialProperty prop, GUIContent label, Rect rect,
                            ChannelMode channleIndex = ChannelMode.none, int index = -1, int len = 0, Action<int> callback = null)
        {
            if (prop == null) return;
            DrawField<int>(editor, prop, width: 20, isEditor: false, rectf: () => rect, args: new DTFieldArgs<int>()//DTGUIEx.GetRect(18, DTGUIEx.kLineHeight, GUILayout.ExpandWidth(false))
            {
                InDeCode = (val) => { return DTUtil.GetBits(val.ToInt(channleIndex), index, len); },
                OutDeCode = (val, prop) =>
                {
                    DTchannelDate tmp = prop.GetValue();
                    val = DTUtil.ReplaceBits(val, tmp.ToInt(channleIndex), index, len);
                    prop.SetValue(tmp.Set(channleIndex, val), channleIndex);
                },
                DrawGUI = (rect, val) =>
                                {
                                    int indent = EditorGUI.indentLevel;
                                    EditorGUI.indentLevel = 0;
                                    Rect btr = rect;
                                    int v = EditorGUI.Toggle(btr, val > 0, new GUIStyle("minibutton")) ? 1 : 0;
                                    btr.x += 1;
                                    EditorGUI.LabelField(btr, label, new GUIStyle(EditorStyles.iconButton)
                                    {
                                        alignment = TextAnchor.MiddleCenter,
                                        fontSize = 12
                                    });
                                    EditorGUI.indentLevel = indent;
                                    return v;
                                },
                DrawEditorGUI = (val) =>
                {
                    int indent = EditorGUI.indentLevel;
                    EditorGUI.indentLevel = 0;
                    int v = EditorGUILayout.Toggle(val > 0,
                        new GUIStyle("minibutton"), GUILayout.Width(18), GUILayout.Height(16), GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(false)) ? 1 : 0;
                    Rect btr = GUILayoutUtility.GetLastRect();
                    btr.x += 1;
                    EditorGUI.LabelField(btr, label, new GUIStyle(EditorStyles.iconButton)
                    {
                        alignment = TextAnchor.MiddleCenter,
                        fontSize = 12
                    });
                    EditorGUI.indentLevel = indent;
                    return v;
                },
                Callback = callback
            });
        }


        public static void ToggleBTNShaderProperty(this MaterialEditor editor, MaterialProperty prop, GUIContent label,
                            ChannelMode channleIndex = ChannelMode.none, int index = -1, int len = 0, Action<int> callback = null)
        {
            if (prop == null) return;
            DrawField<int>(editor, prop, width: 20, isEditor: true, args: new DTFieldArgs<int>() //DTGUIEx.GetRect(18, DTGUIEx.kLineHeight, GUILayout.ExpandWidth(false))
            {
                InDeCode = (val) => { return DTUtil.GetBits(val.ToInt(channleIndex), index, len); },
                OutDeCode = (val, prop) =>
                {
                    DTchannelDate tmp = prop.GetValue();
                    val = DTUtil.ReplaceBits(val, tmp.ToInt(channleIndex), index, len);
                    prop.SetValue(tmp.Set(channleIndex, val), channleIndex);
                },
                DrawGUI = (rect, val) =>
                                {
                                    int indent = EditorGUI.indentLevel;
                                    EditorGUI.indentLevel = 0;
                                    Rect btr = rect;
                                    int v = EditorGUI.Toggle(btr, val > 0, new GUIStyle("minibutton")) ? 1 : 0;
                                    btr.x += 1;
                                    EditorGUI.LabelField(btr, label, new GUIStyle(EditorStyles.iconButton)
                                    {
                                        alignment = TextAnchor.MiddleCenter,
                                        fontSize = 12
                                    });
                                    EditorGUI.indentLevel = indent;
                                    return v;
                                },
                DrawEditorGUI = (val) =>
                {
                    int indent = EditorGUI.indentLevel;
                    EditorGUI.indentLevel = 0;
                    int v = EditorGUILayout.Toggle(val > 0,
                        new GUIStyle("minibutton"), GUILayout.Width(18), GUILayout.Height(16), GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(false)) ? 1 : 0;
                    Rect btr = GUILayoutUtility.GetLastRect();
                    btr.x += 1;
                    EditorGUI.LabelField(btr, label, new GUIStyle(EditorStyles.iconButton)
                    {
                        alignment = TextAnchor.MiddleCenter,
                        fontSize = 12
                    });
                    EditorGUI.indentLevel = indent;
                    return v;
                },
                Callback = callback
            });
        }
        public static bool DrawIconButton(this MaterialEditor editor, GUIContent label)
        {
            GUIStyle gUIStyle = new GUIStyle(EditorStyles.iconButton);
            if (string.IsNullOrEmpty(label.text))
                gUIStyle.imagePosition = ImagePosition.ImageOnly;
            gUIStyle.alignment = TextAnchor.MiddleCenter;
            gUIStyle.fontSize = 12;
            bool tmp = GUILayout.Button(GUIContent.none, EditorStyles.miniButton, GUILayout.Width(18), GUILayout.Height(16), GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(false));
            Rect btr = GUILayoutUtility.GetLastRect();
            btr.x += 1;
            EditorGUI.LabelField(btr, label, gUIStyle);
            return tmp;
        }
        #endregion
        #region  HEAD LABLE
        public static void DrawHeadLable(GUIContent label, float hight = 16)
        {
            int indent = EditorGUI.indentLevel;
            EditorGUI.indentLevel = 0;
            Rect rect = EditorGUILayout.GetControlRect(true, hight);
            rect.x += 14 * indent;
            rect.width -= 14 * indent;
            rect.height = 1;
            EditorGUI.DrawRect(rect, new Color(0, 0, 0, 0.3f));
            rect.height = 18;
            EditorGUI.DrawRect(rect, new Color(0, 0, 0, 0.12f));
            // EditorGUI.indentLevel++;
            EditorGUI.LabelField(rect, label, new GUIStyle(EditorStyles.boldLabel) { fontSize = 12, alignment = TextAnchor.MiddleCenter });
            // EditorGUI.indentLevel--;
            rect.y += 17;
            rect.height = 1;
            EditorGUI.indentLevel = indent;
        }
        #endregion

        #region  FLODOUT
        public static bool DrawFoldout(GUIContent label, bool value, bool toggleOnLabelClick = false, float hight = 16, bool islast = false)
        {
            int indent = EditorGUI.indentLevel;
            EditorGUI.indentLevel = 0;
            Rect rect = EditorGUILayout.GetControlRect(true, hight);
            rect.x += 14 * indent;
            rect.width -= 14 * indent;
            rect.height = 1;
            EditorGUI.DrawRect(rect, new Color(0, 0, 0, 0.3f));
            rect.height = 18;
            EditorGUI.DrawRect(rect, new Color(0, 0, 0, 0.12f));
            EditorGUI.indentLevel++;
            bool v = EditorGUI.Foldout(rect, value, label, toggleOnLabelClick, new GUIStyle("Foldout") { fontSize = 12 });
            EditorGUI.indentLevel--;
            rect.y += 17;
            rect.height = 1;
            if (v)
            {
                EditorGUI.DrawRect(rect, new Color(0, 0, 0, 0.3f));
                GUILayoutUtility.GetRect(0, 1, GUILayout.ExpandWidth(true));
            }
            if (islast && !v)
            {
                EditorGUI.DrawRect(rect, new Color(0, 0, 0, 0.3f));
            }
            EditorGUI.indentLevel = indent;
            return v;
        }
        #endregion



        #region  CONTROL

        public static void DrawLastFieldLable(this MaterialProperty prop, GUIContent viewLabel, RectOffset margin = null)
        {
            if (prop == null) return;
            Rect rect = GUILayoutUtility.GetLastRect();
            if (margin != null)
            {
                rect.x += margin.left;
                rect.y += margin.top;
                rect.width -= margin.horizontal;
                rect.height -= margin.vertical;
            }
            int indent = EditorGUI.indentLevel;
            EditorGUI.indentLevel = 0;
            viewLabel.text = "  " + viewLabel.text;
            EditorGUI.LabelField(rect, viewLabel, new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleLeft, fontSize = 12 });
            // EditorGUI.DrawRect(rect, new Color(0, 0, , 1f));
            EditorGUI.indentLevel = indent;
        }

        public static void DrawLine(Func<Rect> rectFunc)
        {
            DrawLine(rectFunc, new Color(0, 0, 0, 0.2f));
        }
        public static void DrawLine(Func<Rect> rectFunc, Color lineColor)
        {
            if (rectFunc == null) return;
            // Rect r = GUILayoutUtility.GetRect(100, height, GUILayout.ExpandWidth(true));
            // r.height = 1;
            EditorGUI.DrawRect(rectFunc(), lineColor);
        }
        public static void DrawLine(float height)
        {
            DrawLine(height, new Color(0, 0, 0, 0.2f));
        }
        public static void DrawLine(float height, Color lineColor)
        {
            Rect r = GUILayoutUtility.GetRect(100, height, GUILayout.ExpandWidth(true));
            r.height = 1;
            EditorGUI.DrawRect(r, lineColor);
        }


        #endregion

        /*
        ===================================================================================================================================















        ===================================================================================================================================
        */
        public static MaterialProperty GetProperty(Material material, string name)
        {
            return null;
        }


        public static GUIContent DTGetContent(string label = "", string tooltip = "", string icon = "")
        {
            if (string.IsNullOrEmpty(label) && string.IsNullOrEmpty(tooltip) && string.IsNullOrEmpty(icon))
            {
                return GUIContent.none;
            }
            Texture texture = null;
            if (!string.IsNullOrEmpty(icon))
            {
                texture = EditorGUIUtility.IconContent(icon).image;
            }

            GUIContent gUIContent = new GUIContent(label, texture, tooltip);
            return gUIContent;
        }
        public static Rect GetRect(float propHight)
        {
            return GUILayoutUtility.GetRect(EditorGUIUtility.fieldWidth + EditorGUIUtility.labelWidth, propHight, EditorStyles.layerMaskField, GUILayout.ExpandWidth(true));
        }

        public static GUIContent DTGetAnimContent(string label = "", string tooltip = "")
        {
            GUIContent content = DTGetContent(label, tooltip, EditorGUIUtility.isProSkin ? "d_UndoHistory" : "UndoHistory");
            return content;
        }

        public static void ClearMaterial(this Material o)
        {
            Material mat = o as Material;
            if (mat == null)
            {
                return;
            }
            SerializedObject psSource = new SerializedObject(mat);

            SerializedProperty ShaderKeywords = psSource.FindProperty("m_ShaderKeywords");
            CleanMaterialShaderKeywords(ShaderKeywords, mat);

            SerializedProperty emissionProperty = psSource.FindProperty("m_SavedProperties");
            SerializedProperty texEnvs = emissionProperty.FindPropertyRelative("m_TexEnvs");
            SerializedProperty floats = emissionProperty.FindPropertyRelative("m_Floats");
            SerializedProperty colos = emissionProperty.FindPropertyRelative("m_Colors");
            CleanMaterialSerializedProperty(texEnvs, mat);
            CleanMaterialSerializedProperty(floats, mat);
            CleanMaterialSerializedProperty(colos, mat);
            psSource.ApplyModifiedProperties();
            EditorUtility.SetDirty(o);
            Debug.Log("清理完成!!");
        }

        static void CleanMaterialSerializedProperty(SerializedProperty property, Material mat)
        {
            for (int j = property.arraySize - 1; j >= 0; j--)
            {
                string propertyName = property.GetArrayElementAtIndex(j).FindPropertyRelative("first").stringValue;
                if (!mat.HasProperty(propertyName))
                {
                    property.DeleteArrayElementAtIndex(j);
                }
            }
        }
        static void CleanMaterialShaderKeywords(SerializedProperty property, Material mat)
        {

            if (DTUtil.GetShaderKeywords(mat.shader, out var global, out var local))
            {
                HashSet<string> keywords = new HashSet<string>();
                foreach (var g in global)
                {
                    keywords.Add(g);
                }
                foreach (var l in local)
                {
                    keywords.Add(l);
                }
                //重置keywords
                List<string> resetKeywords = new List<string>(mat.shaderKeywords);
                foreach (var item in mat.shaderKeywords)
                {
                    if (!keywords.Contains(item))
                        resetKeywords.Remove(item);
                }
                mat.shaderKeywords = resetKeywords.ToArray();
            }
        }
    }
    #endregion // DTGUIEx

    #region DTGUIArgs
    public class DTGUIArgs
    {
        public static readonly DTGUIArgs Empty = new DTGUIArgs();
    }
    public class DECodeArgs<T> : DTGUIArgs
    {
        public Func<T, object> InDeCode;
        public Func<T, object> OutDeCode;
        public Action<T, object> OnChange;

        public void Check()
        {
            if (InDeCode == null)
            {
                InDeCode = (v) => { return v; };
            }
            if (OutDeCode == null)
            {
                OutDeCode = (v) => { return v; };
            }
            if (OnChange == null)
            {
                OnChange = (v, obj) => { };
            }
        }
    }
    // public interface IModuleBtnGroup
    // {
    //     public int bitIndex { get; set; }
    //     public string KeyName { get; set; }
    //     public string BtnDisplayName { get; set; }
    //     public string ToolTip { get; set; }
    //     public string IconName { get; set; }
    //     public bool IsEnable { get; set; }
    // }
    public class DT_Module : DTGUIArgs//, IModuleBtnGroup
    {
        public int bitIndex
        {
            get => (int)KeyName.ToEnum<ModuleKey>(ModuleKey.None);
            set { bitIndex = (int)KeyName.ToEnum<ModuleKey>(ModuleKey.None); }
        }
        public string KeyName { get; set; }
        public string DisplayName;
        public string BtnDisplayName { get; set; }
        public string ToolTip { get; set; }
        public string IconName { get; set; }
        public bool IsEnable { get; set; }
        public bool IsViewnFoldout;
        public int ViewBtnCount;
        GUIContent _displayLable;
        public Action<DT_HeaderScope, MaterialEditor, MaterialProperty[]> DrawGUI;
        /// <summary>
        /// 模块加载
        /// </summary>
        /// <param name="disN">模块显示名称</param>
        /// <param name="btnN">模块按钮名称</param>
        /// <param name="tip">模块提示</param>
        /// <param name="fbtn">模块是否包含提示按钮，单位是数量，20像素每个，如果是宽按钮要按像素翻倍。主要是提示报警图标按钮</param>
        /// <param name="isE">模块是否启用</param>
        /// <param name="DrFun">模块绘制函数</param>
        public DT_Module(string key, int fbtn = 0, bool isv = false, bool isE = true, (string, string, string, string) disN = default)
        {
            KeyName = key;
            DisplayName = disN.Item1;
            ToolTip = disN.Item2;
            IconName = disN.Item3;
            BtnDisplayName = disN.Item4;
            IsEnable = isE;
            IsViewnFoldout = isv; //(disN.StartsWith("Main") || disN.StartsWith("Global") || disN.StartsWith("YF")) ? true : false;
            DrawGUI = null;
            ViewBtnCount = fbtn;

        }
        public void Draw(MaterialEditor materialEditor, MaterialProperty[] props)
        {
            if (DrawGUI != null && IsEnable)
            {
                _displayLable = DTGUIEx.DTGetContent(DisplayName, ToolTip, IconName);
                if (IconName == "d_Image Icon" || IconName == "d_RawImage Icon")
                {
                    EditorGUIUtility.SetIconSize(new Vector2(16, 18));
                }
                else
                {
                    EditorGUIUtility.SetIconSize(new Vector2(16, 16));
                }
                using (var header = new DT_HeaderScope(_displayLable, IsViewnFoldout, materialEditor, ViewBtnCount))
                {
                    DrawGUI(header, materialEditor, props);
                    IsViewnFoldout = header.expanded;
                }
            }
        }
    }
    public class DTFieldArgs<T> : DTGUIArgs
    {
        // DTchannelDate
        public Func<DTchannelDate, object> InDeCode;
        public Action<T, MaterialProperty> OutDeCode;
        public Func<Rect, T, T> DrawGUI;
        public Action<T> Callback;

        public Func<T, T> DrawEditorGUI;

        public void Check()
        {
            if (InDeCode == null)
            {
                InDeCode = (prop) => { return prop; };
            }
            if (OutDeCode == null)
            {
                OutDeCode = (obj, prop) => { };
            }
            if (DrawGUI == null)
            {
                DrawGUI = (rect, val) => { return val; };
            }
            if (DrawEditorGUI == null)
            {
                DrawEditorGUI = (val) => { return val; };
            }
            if (Callback == null)
            {
                Callback = (a) => { };
            }
        }

        object GetProp(MaterialProperty prop)
        {
            switch (prop.type)
            {
                case MaterialProperty.PropType.Color: return prop.colorValue;
                case MaterialProperty.PropType.Float: return prop.floatValue;
                case MaterialProperty.PropType.Range: return prop.floatValue;
                case MaterialProperty.PropType.Vector: return prop.vectorValue;
                case MaterialProperty.PropType.Texture: return prop.textureValue;
                case MaterialProperty.PropType.Int: return prop.intValue;
            }
            return null;
        }
    }
    #endregion  // DTGUIArgs

    public class Bitkey
    {
        public string PropKey;
        public int index;
        public int len;
        public ChannelMode mode;
        public GUIContent lable;
        public bool isView;
        public Bitkey()
        {
            PropKey = "";
            index = -1;
            len = -1;
            mode = ChannelMode.none;
            lable = GUIContent.none;
            isView = false;
        }
        public Bitkey(string propKey, int index, int len, ChannelMode mode = ChannelMode.none, bool isv = true, GUIContent gUIContent = null)
        {
            PropKey = propKey;
            this.index = index;
            this.len = len;
            this.mode = mode;
            lable = gUIContent;
            isView = isv;
        }
        public MaterialProperty GetProp(Dictionary<string, MaterialProperty> date)
        {
            if (!date.ContainsKey(PropKey)) return null;
            return date[PropKey];
        }
        public int GetValue(Dictionary<string, MaterialProperty> date)
        {
            if (!date.ContainsKey(PropKey)) return 0;
            MaterialProperty prop = date[PropKey];
            if (prop == null) return 0;
            return DTUtil.GetBits(new DTchannelDate(prop).ToInt(mode), index, len);
        }
        public void SetValue(Dictionary<string, MaterialProperty> date, int value)
        {
            if (!date.ContainsKey(PropKey)) return;
            MaterialProperty prop = date[PropKey];
            if (prop == null) return;
            DTchannelDate tmp = new DTchannelDate(prop);
            int val = DTUtil.ReplaceBits(value, tmp.ToInt(mode), index, len);
            prop.SetValue(tmp.Set(mode, val));
        }

        public int GetLen()
        {
            return (1 << len) - 1;
        }
    }
}